//==============================================================================
// Copyright (c) 2008-2013 Niall Ryan. All Rights Reserved.
//==============================================================================

#ifndef HYDRA_ALLOCATORS_DELAYEDFREEALLOCATOR_H
#define HYDRA_ALLOCATORS_DELAYEDFREEALLOCATOR_H 1

#include <vector>
#include "hydra/locks/TTASLock.h"

namespace Hydra
{
	/**
	 * A basic implementation of an allocator, uses the standard malloc/free to allocate, but delays the free until a later point. It is
     * therefore not vulnerable to the ABA problem.
	 *
	 * Released pointers are simply added to a list and not freed. The user must free the released allocations manually by calling
     * freeReleasedAllocs, at a time when the user is certain none of the released allocations are being used. The reference counting
     * functions are not implemented.
	 */
	class DelayedFreeAllocator
	{
	public:
        static const bool kAbaVulnerable = false;
        static const bool kAllowsDanglingPointers = true;

		static void* alloc(size_t numBytes)
		{
			return ::malloc(numBytes);
		}

		template<typename T>
		static T* create()
		{
			T* item = static_cast<T*>(alloc(sizeof(T)));
			new(item) T;
			return item;
		}

		template<typename T>
		static T* createArray(size_t numElements)
		{
			T* arr = static_cast<T*>(alloc(sizeof(T)*numElements));
			for (unsigned int i=0; i<numElements; ++i)
				new(&arr[i]) T;
			return arr;
		}

		static void free(void* ptr)
		{
			m_lock.lock();
			m_released.push_back(ptr);
			m_lock.unlock();
		}

		template<typename T>
		static void destroy(T* item)
        {
            item->~T();
			m_lock.lock();
			m_released.push_back(item);
			m_lock.unlock();
        }

		template<typename T>
		static void destroyArray(T* arr, size_t numElements)
		{
			for (unsigned int i=0; i<numElements; ++i)
				arr[i].~T();
			m_lock.lock();
			m_released.push_back(arr);
			m_lock.unlock();
		}

		static void freeReleasedAllocs()
		{
			m_lock.lock();
			for( unsigned int i=0; i<m_released.size(); ++i )
				::free(m_released[i]);
			m_released.clear();
			m_lock.unlock();
		}

	private:
		static TTASLock m_lock;

		typedef std::vector<void*> ReleasedList;
		static ReleasedList m_released;
	};
}

#endif
